//
//  Unsafe.m
//  PezMonkey
//
//  Created by Spence DiNicolantonio on 12/25/09.
//  Copyright 2009 Necrosoft. All rights reserved.
//

#import "Unsafe.h"

#define kUnsafeShapeCount 30
#define kUnsafeShapeSize 20
#define kUnsafeShapeSizeMin 5
#define kUnsafeShapeSizeDelta 1

@implementation Unsafe

@synthesize staticBody;
@synthesize shapeSize;

- (NSString *)demoName {
	return @"Unsafe Opps";
}

- (void)start {
	// create space
	self.space = [PMSpace spaceWithGravity:PMVectMake(0, -1)];
	[space setSubSteps:3];
	[space setIterations:50];
	[space setActiveHashSize:999 cellDimension:40];
	[space setStaticHashSize:2999 cellDimension:30];
	
	// initialize shared static body
	self.staticBody = [PMBody staticBody];
	
	// initialize shape size
	self.shapeSize = kUnsafeShapeSize;
	
	// create screen boundary
	[self addScreenBoundary];
	
	
	// determine shapes to make
	NSUInteger shapeCounts[3] = {
		kUnsafeShapeCount / 3,
		kUnsafeShapeCount / 3,
		kUnsafeShapeCount - (2*kUnsafeShapeCount / 3)
	};
		
	// create circles
	for (int i=0; i<shapeCounts[0]; i++) {
		[self addCircle];
	}
	
	// create squares
	for (int i=0; i<shapeCounts[1]; i++) {
		[self addPolygonWithSides:4];
	}
	
	// create triangles
	for (int i=0; i<shapeCounts[2]; i++) {
		[self addPolygonWithSides:3];
	}
}

- (void)update:(NSTimeInterval)deltaT {
	// no change?
	if (!(upArrow ^ downArrow))
		return;
		
	// determine change in shape size
	PMFloat delta = (upArrow)? kUnsafeShapeSizeDelta : -kUnsafeShapeSizeDelta;
	
	// determine new shape size
	PMFloat newSize = shapeSize + delta;
	
	// resize shapes
	for (PMShape *shape in space.activeShapes) {
		PMBody *body = shape.body;
		
		// minimum size?
		if (newSize < kUnsafeShapeSizeMin)
			return;
		
		// update shape size
		self.shapeSize = newSize;
		
		// circles:
		if ([shape isKindOfClass:PMCircleShape.class]) {
			[(PMCircleShape *)shape setRadius:newSize];
			PMFloat moment = PMMomentForCircle([body mass], 0, newSize, PMVectZero);
			[body setMoment:moment];
		} 
		
		// polygons:
		else {
			NSUInteger sides = [(PMPolygonShape *)shape vertCount];
			PMVect verts[sides];
			for (int i=0; i<sides; i++) {
				PMFloat angle = -2*M_PI*i/((PMFloat) sides);
				PMVect vert = PMVectMake(newSize * cos(angle), 
										 newSize * sin(angle));
				verts[i] = vert;
			}
			[(PMPolygonShape *)shape setVertices:verts vertCount:sides];
			PMFloat moment = PMMomentForPolygon([body mass], verts, sides, PMVectZero);
			[body setMoment:moment];
		}
	}
}

/**
 * Creates a circle with random position and adds it to the space.
 */
- (void)addCircle {
	// define circle attributes
	PMFloat mass = 1;
	PMFloat moment = PMMomentForCircle(mass, 0, kUnsafeShapeSize, PMVectZero);
	
	// create circle body 
	PMBody *body = [PMBody bodyWithMass:mass moment:moment];
	PMFloat xPos = (PMFloatRandom() * (640 - shapeSize/2)) - (320 - shapeSize/2);
	PMFloat yPos = (PMFloatRandom() * (480 - shapeSize/2)) - (240 - shapeSize/2);
	[body setPosition:PMVectMake(xPos, yPos)];
	[space addBody:body];
	
	// create circle shape
	PMShape *shape = [PMCircleShape circleShapeWithBody:body radius:kUnsafeShapeSize];
	[shape setFriction:1.0];
	[shape setElasticity:0.0];
	[space addShape:shape];
	
	// store shape reference in body's data pointer
	body.data = shape;
}

/**
 * Creates a square with a given number of sides and random position and adds it 
 * to the space.
 */
- (void)addPolygonWithSides:(NSUInteger)sides;
 {
	// define polygon vertices
	PMVect verts[sides];
	for (int i=0; i<sides; i++) {
		PMFloat angle = -2*M_PI*i/((PMFloat) sides);
		PMVect vert = PMVectMake(kUnsafeShapeSize * cos(angle), 
								 kUnsafeShapeSize * sin(angle));
		verts[i] = vert;
	}
	
	// define polygon attributes
	PMFloat mass = 1;
	PMFloat moment = PMMomentForPolygon(mass, verts, sides, PMVectZero);
	
	// create polygon body 
	PMBody *body = [PMBody bodyWithMass:mass moment:moment];
	PMFloat xPos = (PMFloatRandom() * (640 - shapeSize/2)) - (320 - shapeSize/2);
	PMFloat yPos = (PMFloatRandom() * (480 - shapeSize/2)) - (240 - shapeSize/2);
	[body setPosition:PMVectMake(xPos, yPos)];
	[space addBody:body];
	
	// create polygon shape
	PMShape *shape = [PMPolygonShape polygonShapeWithBody:body 
												 vertices:verts 
											  vertCount:sides];
	[shape setFriction:1.0];
	[shape setElasticity:0.0];
	[space addShape:shape];
	 
	 // store shape reference in body's data pointer
	 body.data = shape;
}

/**
 * Creates and segment shapes to demo space to make a boundary along the edges of
 * the screen.
 */
- (void)addScreenBoundary {
	// make sure static body has been initialized
	if (!staticBody)
		self.staticBody = [PMBody staticBody];
	
	// create temp shape
	PMShape *shape;
	
	// define boundary segment width 
	// (needs to be thick to avoid penetration at high velocities)
	PMFloat width = 30;
	
	PMFloat vertCount = 4;
	
	// create left screen boundary
	PMVect leftVerts[] = {
		PMVectMake(-320 - width, -240),
		PMVectMake(-320 - width, 240),
		PMVectMake(-320, 240),
		PMVectMake(-320, -240)
	};
	shape = [PMPolygonShape polygonShapeWithBody:staticBody 
										vertices:leftVerts 
									 vertCount:vertCount];
	[shape setElasticity:1.0];
	[shape setFriction:1.0];
	[shape setLayers:PMLayerMaskNotGrabbable];
	[space addStaticShape:shape];
	
	// create right screen boundary
	PMVect rightVerts[] = {
		PMVectMake(320 + width, 240),
		PMVectMake(320 + width, -240),
		PMVectMake(320, -240),
		PMVectMake(320, 240),
	};
	shape = [PMPolygonShape polygonShapeWithBody:staticBody 
										vertices:rightVerts 
									 vertCount:vertCount];
	[shape setElasticity:1.0];
	[shape setFriction:1.0];
	[shape setLayers:PMLayerMaskNotGrabbable];
	[space addStaticShape:shape];
	
	// create bottom screen boundary
	PMVect bottomVerts[] = {
		PMVectMake(-320, -240 - width),
		PMVectMake(-320, -240),
		PMVectMake(320, -240),
		PMVectMake(320, -240 - width),
	};
	shape = [PMPolygonShape polygonShapeWithBody:staticBody 
										vertices:bottomVerts 
									 vertCount:vertCount];
	[shape setElasticity:1.0];
	[shape setFriction:1.0];
	[shape setLayers:PMLayerMaskNotGrabbable];
	[space addStaticShape:shape];
}


#pragma mark -
#pragma mark Dealloc

- (void)dealloc {
	[staticBody release];
	[super dealloc];
}

@end
